// xMain.cpp: Implementierung der Klasse xMain.
//
//////////////////////////////////////////////////////////////////////

#include "windows.h"
#include <string>
#include <list>
using namespace std;

#include "../lsapi/lsapi.h"
#include "../SDK/xExports.h"

#include "xWindow.h"

typedef list<string> StringList;
typedef list<string>::iterator StringListIterator;

typedef list<xWindow *> xWindowList;
typedef list<xWindow *>::iterator xWindowListIterator;

#define StringCchCopy(a, b, c) strncpy((a), (c), (b))
#define StringCchLength(a, b, c) *(c) = strlen((a)), S_OK
#define StringCchPrintf _snprintf

void BangAlwaysOnTop(HWND hwndCaller, LPCSTR pszArgs);
void BangMove(HWND hwndCaller, LPCSTR pszArgs);
void BangMoveBy(HWND hwndCaller, LPCSTR pszArgs);
void BangReposition(HWND hwndCaller, LPCSTR pszArgs);
void BangRepositionBy(HWND hwndCaller, LPCSTR pszArgs);
void BangResize(HWND hwndCaller, LPCSTR pszArgs);
void BangResizeBy(HWND hwndCaller, LPCSTR pszArgs);
void BangHide(HWND hwndCaller, LPCSTR pszArgs);
void BangShow(HWND hwndCaller, LPCSTR pszArgs);
void BangToggle(HWND hwndCaller, LPCSTR pszArgs);

int screenWidth = 0;
int screenHeight = 0;

int bitsperpixel = 8;

#define V_AUTHOR "You"
#define V_NAME "xTemplate"
#define V_VERSION "0.1"

xWindow *lookupxWindow( const string &name );

HINSTANCE hInstance;
HWND messageHandler;

xWindowList xwindowList;

int lsMessages[] = {
	LM_GETREVID,
	0
};

LRESULT WINAPI MessageHandlerProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch(message)
	{
		case LM_GETREVID:
		{
			UINT uLength;
			StringCchPrintf((char*)lParam, 64, "%s.dll: %s", V_NAME, V_VERSION);
			
			if (SUCCEEDED(StringCchLength((char*)lParam, 64, &uLength)))
				return uLength;

			lParam = NULL;
			return 0;
		}
	}

	return DefWindowProc(hWnd, message, wParam, lParam);
}

BOOL __stdcall _DllMainCRTStartup(HINSTANCE hInst, DWORD fdwReason, LPVOID lpvRes)
{
	// We don't need thread notifications for what we're doing.  Thus, get
	// rid of them, thereby eliminating some of the overhead of this DLL
	DisableThreadLibraryCalls(hInst);

	return TRUE;
}

int initModuleEx(HWND hParent, HINSTANCE hInstance, const char *lsPath)
{
	WNDCLASSEX wc;

	wc.cbSize = sizeof(WNDCLASSEX);
	wc.style = CS_GLOBALCLASS | CS_DBLCLKS ;
	wc.lpfnWndProc = xWindow::windowProcedure;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = sizeof(xWindow *);
	wc.hInstance = hInstance;
	wc.hbrBackground = 0;
	wc.hCursor = LoadCursor(0, IDC_ARROW);
	wc.hIcon = 0;
	wc.lpszMenuName = 0;
	wc.lpszClassName = "xWindow";
	wc.hIconSm = 0;

	RegisterClassEx(&wc);

	wc.cbSize = sizeof(WNDCLASSEX);
	wc.style = CS_GLOBALCLASS;
	wc.lpfnWndProc = MessageHandlerProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = hInstance;
	wc.hbrBackground = 0;
	wc.hCursor = 0;
	wc.hIcon = 0;
	wc.lpszMenuName = 0;
	wc.lpszClassName = "xWindowMessageHandler";
	wc.hIconSm = 0;

	RegisterClassEx(&wc);

	messageHandler = CreateWindowEx(WS_EX_TOOLWINDOW,
		"xWindowMessageHandler",
		0,
		WS_POPUP,
		0, 0, 0, 0, 
		0,
		0,
		hInstance,
		0);

	if (!messageHandler)
		return 1;

	SendMessage(GetLitestepWnd(),
		LM_REGISTERMESSAGE,
		(WPARAM) messageHandler,
		(LPARAM) lsMessages);

	::hInstance = hInstance;

	screenWidth = GetSystemMetrics(SM_CXSCREEN);
	screenHeight = GetSystemMetrics(SM_CYSCREEN);

	DEVMODE DevMode;
	DevMode.dmSize=sizeof(DEVMODE);
		
	if (EnumDisplaySettings(NULL,ENUM_CURRENT_SETTINGS,&DevMode))
	{
		bitsperpixel = (int) DevMode.dmBitsPerPel;
	}

	AddBangCommand("!xWindowAlwaysOnTop", BangAlwaysOnTop);
	AddBangCommand("!xWindowHide", BangHide);
	AddBangCommand("!xWindowMove", BangMove);
	AddBangCommand("!xWindowMoveBy", BangMoveBy);
	AddBangCommand("!xWindowReposition", BangReposition);
	AddBangCommand("!xWindowRepositionBy", BangRepositionBy);
	AddBangCommand("!xWindowResize", BangResize);
	AddBangCommand("!xWindowResizeBy", BangResizeBy);
	AddBangCommand("!xWindowShow", BangShow);
	AddBangCommand("!xWindowToggle", BangToggle);

	StringList xWindowItems;

	LPVOID f = LCOpen(NULL);

	if(f != NULL)
	{
		char lineBuffer[MAX_LINE_LENGTH] = {0};

		while(LCReadNextConfig(f, "*xWindow", lineBuffer, MAX_LINE_LENGTH))
		{
			char temp[64], configline[MAX_LINE_LENGTH];
			char *buffers[] = {temp};
			
			if(LCTokenize(lineBuffer, buffers, 1, configline) >= 1)
			{
				xWindowItems.insert(xWindowItems.end(), (string) configline);
			}
		}
		
		LCClose(f);
	}

	char name[64], path[MAX_LINE_LENGTH];
	char *buffers[] = {name, path};
	
	for (StringListIterator sit = xWindowItems.begin(); sit != xWindowItems.end(); sit++)
	{
		LCTokenize((*sit).c_str(), buffers, 2, NULL);

		xWindow *xwindow = new xWindow((string)name);
		xwindow->load(hInstance);
	
		if ( xwindow )
		{	
			xwindowList.insert(xwindowList.end(), xwindow);

			//Initial Show
			if (!xwindow->isStartHidden())
				xwindow->show();
			else
				xwindow->hide();
		}
	}

	return 0;
}

void quitModule(HINSTANCE hInstance)
{
	RemoveBangCommand("!xWindowAlwaysOnTop");
	RemoveBangCommand("!xWindowHide");
	RemoveBangCommand("!xWindowMove");
	RemoveBangCommand("!xWindowMoveBy");
	RemoveBangCommand("!xWindowReposition");
	RemoveBangCommand("!xWindowRepositionBy");
	RemoveBangCommand("!xWindowResize");
	RemoveBangCommand("!xWindowResizeBy");
	RemoveBangCommand("!xWindowShow");
	RemoveBangCommand("!xWindowToggle");

	for (xWindowListIterator lit = xwindowList.begin(); lit != xwindowList.end(); lit++)
	{
		(*lit)->hide();
		delete *lit;
	}

	xwindowList.clear();

	SendMessage(GetLitestepWnd(),
		LM_UNREGISTERMESSAGE,
		(WPARAM) messageHandler,
		(LPARAM) lsMessages);

	DestroyWindow(messageHandler);

	UnregisterClass("xWindow", hInstance);
	UnregisterClass("xWindowMessageHandler", hInstance);
}

xWindow *lookupxWindow( const string &name )
{
	for (xWindowListIterator lit = xwindowList.begin(); lit != xwindowList.end(); lit++)
	{
		if (_stricmp(name.c_str(), (*lit)->getName().c_str()) == NULL)
			return *lit;
	}

	return NULL;
}

// !...AlwaysOnTop <xwindow> <on|true, off|false, toggle>
void BangAlwaysOnTop(HWND hwndCaller, LPCSTR pszArgs)
{
	char name[64], option[16];
	char *buffers[] = {name, option};

	LCTokenize(pszArgs, buffers, 2, NULL);

	xWindow *xwindow = lookupxWindow(name);

	if (xwindow)
	{
		if (_stricmp(option, "false") == NULL || _stricmp(option, "off") == NULL)
			xwindow->setAlwaysOnTop(false);
		else if (_stricmp(option, "toggle") == NULL || _stricmp(option, "switch") == NULL)
			xwindow->setAlwaysOnTop(!xwindow->isAlwaysOnTop());
		else
			xwindow->setAlwaysOnTop(true);
	}
}

// !...Show <xwindow>
void BangShow(HWND hwndCaller, LPCSTR pszArgs)
{
	xWindow *xwindow = lookupxWindow(pszArgs);
	
	if (xwindow)
	{
		xwindow->show();
	}
}

// !...Hide <xwindow>
void BangHide(HWND hwndCaller, LPCSTR pszArgs)
{
	xWindow *xwindow = lookupxWindow(pszArgs);
	
	if (xwindow)
	{
		xwindow->hide();
	}
}

// !...Toggle <xwindow>
void BangToggle(HWND hwndCaller, LPCSTR pszArgs)
{
	xWindow *xwindow = lookupxWindow(pszArgs);
	
	if (xwindow)
	{
		if ( xwindow->isVisible() )
			xwindow->hide();
		else
			xwindow->show();
	}
}

// !...Move <xwindow> <x> <y>
void BangMove(HWND hwndCaller, LPCSTR pszArgs)
{
	char name[64], A[16], B[16];
	char *buffers[] = {name, A, B};

	if (LCTokenize(pszArgs, buffers, 3, NULL) >= 3)
	{
		xWindow *xwindow = lookupxWindow(name);

		if (xwindow)
		{
			int x = xParseCoord(A, 0, screenWidth, false);
			int y = xParseCoord(B, 0, screenHeight, true);
				
			xwindow->reposition(1, x, y, 0, 0);
		}
	}
}

// !...MoveBy <xwindow> <x> <y>
void BangMoveBy(HWND hwndCaller, LPCSTR pszArgs)
{
	char name[64], A[16], B[16];
	char *buffers[] = {name, A, B};

	if (LCTokenize(pszArgs, buffers, 3, NULL) >= 3)
	{
		xWindow *xwindow = lookupxWindow(name);

		if (xwindow)
		{
			xwindow->reposition(1, atoi( A ), atoi( B ), 0, 0, true);
		}
	}
}

// !...Reposition <xwindow> <x> <y> <width> <height>
void BangReposition(HWND hwndCaller, LPCSTR pszArgs)
{
	char name[64], A[16], B[16], C[16], D[16];
	char *buffers[] = {name, A, B, C, D};

	if(LCTokenize(pszArgs, buffers, 5, NULL) >= 5)
	{
		xWindow *xwindow = lookupxWindow(name);

		if (xwindow)
		{
			int x = xParseCoord(A, 0, screenWidth, false);
			int y = xParseCoord(B, 0, screenHeight, true);
			int w = xParseDimen(C, 0, screenWidth, false);
			int h = xParseDimen(D, 0, screenHeight, true);
				
			xwindow->reposition(3, x, y, w, h);
		}
	}
}

// !...RepositionBy <xwindow> <cx> <cy> <cwidth> <cheight>
void BangRepositionBy(HWND hwndCaller, LPCSTR pszArgs)
{
	char name[64], A[16], B[16], C[16], D[16];
	char *buffers[] = {name, A, B, C, D};

	if (LCTokenize(pszArgs, buffers, 5, NULL) >= 5)
	{
		xWindow *xwindow = lookupxWindow(name);
		
		if (xwindow)
		{
			int x = atoi( A );
			int y = atoi( B );
			int w = atoi( C );
			int h = atoi( D );

			xwindow->reposition(3, x, y, w, h, true);
		}
	}
}


// !...Resize <xwindow> <width> <height>
void BangResize(HWND hwndCaller, LPCSTR pszArgs)
{
	char name[64], A[16], B[16];
	char *buffers[] = {name, A, B};

	if (LCTokenize(pszArgs, buffers, 3, NULL) >= 3)
	{
		xWindow *xwindow = lookupxWindow(name);

		if (xwindow)
		{
			int w = xParseDimen(A, 0, screenWidth, false);
			int h = xParseDimen(B, 0, screenHeight, true);
				
			xwindow->reposition(2, 0, 0, w, h);
		}
	}
}

// !...ResizeBy <xwindow> <cx> <cy>
void BangResizeBy(HWND hwndCaller, LPCSTR pszArgs)
{
	char name[64], A[16], B[16];
	char *buffers[] = {name, A, B};

	if (LCTokenize(pszArgs, buffers, 3, NULL) >= 3)
	{
		xWindow *xwindow = lookupxWindow(name);

		if (xwindow)
		{
			int cx = atoi( A );
			int cy = atoi( B );

			xwindow->reposition(2, 0, 0, cx, cy, true);
		}
	}
}